﻿using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

namespace Model
{
  public enum DllType
  {
    Model,
    Hotfix
  }


  /// <summary>
  /// OjbectSystem (所有的System都要继承这个类)
  /// </summary>
  /// <typeparam name="T">所对应的Component</typeparam>
  public class ObjectSystem<T> : IObjectSystem
  {
    T Value { get; set; }

    public T Get()
    {
      return Value;
    }

    public void Set(object value)
    {
      this.Value = (T)value;
    }

    public Type Type()
    {
      return typeof(T);
    }
  }


  public sealed class EventSystem
  {
    private Dictionary<ushort, List<IEvent>> dicEventDir = new Dictionary<ushort, List<IEvent>>();

    private Dictionary<DllType, Assembly> dicAssembles = new Dictionary<DllType, Assembly>();
    private Dictionary<Type, IObjectSystem> dicObjectSystem = new Dictionary<Type, IObjectSystem>();

    private Queue<Disposer> qUpdate = new Queue<Disposer>();
    private Queue<Disposer> qUpdate2 = new Queue<Disposer>();

    private Queue<Disposer> qLateUpdate = new Queue<Disposer>();
    private Queue<Disposer> QLateUpdate2 = new Queue<Disposer>();

    private readonly Queue<Disposer> qStart = new Queue<Disposer>();

    private Assembly hotfixAssembly;

    public Assembly HotfixAssembly
    {
      get
      {
        return this.hotfixAssembly;
      }
      set
      {
        this.hotfixAssembly = value;
      }

    }


    /// <summary>
    /// 激活EventSystem
    /// </summary>
    /// <param name="dllType">激活的类型</param>
    /// <param name="assembly">所在的程序集</param>
    public void Enable(DllType dllType, Assembly assembly)
    {
      if (this.dicAssembles.ContainsKey(dllType))
      {
        Debug.Log($"你所要激活的对象{dllType}已存在,不需要激活.  Assembly:{assembly}");
        return;
      }

      this.dicAssembles.Add(dllType, assembly);
      this.Initiate();
    }

    /// <summary>
    /// 启动
    /// </summary>
    public void Initiate()
    {
      Type[] types = DllHelper.GetMonoTypes();

      foreach (var type in types)
      {
        object[] tmpObj = type.GetCustomAttributes(typeof(ObjectSystemAttribute), false);
        if (tmpObj.Length == 0) continue;
        if (!(tmpObj[0] is ObjectSystemAttribute)) continue;
        object obj = Activator.CreateInstance(type);
        IObjectSystem objectSys = obj as IObjectSystem;
        if (objectSys == null)
        {
          Debug.LogError($"组件{type.Name}没有继承IObjectSystem");
        }
        dicObjectSystem.Add(objectSys.Type(), objectSys);
      }

      foreach (var type in types)
      {
        object[] tmpObjs = type.GetCustomAttributes(typeof(EventAttribute), false);
        if (tmpObjs.Length == 0) continue;
        foreach (var item in tmpObjs)
        {
          EventAttribute eventAttribute = (EventAttribute)item;
          object tmp = Activator.CreateInstance(type);
          IEvent tmpEvent = tmp as IEvent;
          if (tmpEvent == null)
          {
            Debug.LogWarning($"{type.Name}没有继承IEvnet , 无法执行事件系统,请检查!");
            continue;
          }
          this.RegisterEvent(eventAttribute.type, tmpEvent);
        }
      }

    }

    #region SendMessage

    public void RegisterEvent(ushort messageType, IEvent iEvent)
    {
      if (!this.dicEventDir.ContainsKey(messageType))
      {
        List<IEvent> tmpList = new List<IEvent>();
        dicEventDir.Add(messageType, tmpList);
      }
      dicEventDir[messageType].Add(iEvent);
    }


    public void Run(ushort messageType)
    {
      List<IEvent> tmpList;
      if (!this.dicEventDir.TryGetValue(messageType, out tmpList)) return;

      foreach (var item in tmpList)
      {
        try
        {
          item?.Handler();
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
    }

    public void Run<T>(ushort messageType, T t)
    {
      List<IEvent> tmpList;
      if (!this.dicEventDir.TryGetValue(messageType, out tmpList)) return;

      foreach (var item in tmpList)
      {
        try
        {
          item?.Handler(t);
        }
        catch (Exception e)
        {

          Debug.LogError(e.ToString());
        }
      }
    }

    public void Run<T1, T2>(ushort messageType, T1 t1, T2 t2)
    {
      List<IEvent> tmpList;
      if (!this.dicEventDir.TryGetValue(messageType, out tmpList)) return;

      foreach (var item in tmpList)
      {
        try
        {
          item?.Handler(t1, t2);
        }
        catch (Exception e)
        {

          Debug.LogError(e.ToString());
        }
      }
    }


    public void Run<T1, T2, T3>(ushort messageType, T1 t1, T2 t2, T3 t3)
    {
      List<IEvent> tmpList;
      if (!this.dicEventDir.TryGetValue(messageType, out tmpList)) return;

      foreach (var item in tmpList)
      {
        try
        {
          item?.Handler(t1, t2, t3);
        }
        catch (Exception e)
        {

          Debug.LogError(e.ToString());
        }
      }
    }

    #endregion


    #region 组件生命周期
    /// <summary>
    /// 将对象添加至队列中(对象调用Awale时,会调用这个方法,将所继承的生命周期添置对应的队列中 )
    /// </summary>
    /// <param name="disposer"></param>
    private void Add(Disposer disposer)
    {
      IObjectSystem objectSystem;
      if (!this.dicObjectSystem.TryGetValue(disposer.GetType(), out objectSystem))
      {
        return;
      }
      if (objectSystem is IStart)
      {
        qStart.Enqueue(disposer);
      }
      if (objectSystem is IUpdate)
      {
        qUpdate.Enqueue(disposer);
      }
      if (objectSystem is ILateUpdate)
      {
        qLateUpdate.Enqueue(disposer);
      }
    }

    #region Awake
    public void Awake(Disposer disposer)
    {
      this.Add(disposer);
      IObjectSystem objectSystem;
      if (!this.dicObjectSystem.TryGetValue(disposer.GetType(), out objectSystem)) throw new Exception($"{disposer.GetType().Name} Not Find");

      IAwake awake = objectSystem as IAwake;
      if (awake == null) throw new Exception($"{disposer.GetType().Name} Not Inherit IAwake");
      objectSystem.Set(disposer);
      awake.Awake();
    }

    public void Awake<T>(Disposer disposer, T t)
    {
      this.Add(disposer);
      IObjectSystem objectSystem;
      if (!this.dicObjectSystem.TryGetValue(disposer.GetType(), out objectSystem)) throw new Exception($"{disposer.GetType().Name} Not Find");

      IAwake<T> awake = objectSystem as IAwake<T>;
      if (awake == null) throw new Exception($"{disposer.GetType().Name} Not Inherit IAwake");
      objectSystem.Set(disposer);
      awake.Awake(t);
    }

    public void Awake<T1, T2>(Disposer disposer, T1 t1, T2 t2)
    {
      this.Add(disposer);
      IObjectSystem objectSystem;
      if (!this.dicObjectSystem.TryGetValue(disposer.GetType(), out objectSystem)) throw new Exception($"{disposer.GetType().Name} Not Find");

      IAwake<T1, T2> awake = objectSystem as IAwake<T1, T2>;
      if (awake == null) throw new Exception($"{disposer.GetType().Name} Not Inherit IAwake");
      objectSystem.Set(disposer);
      awake.Awake(t1, t2);
    }


    public void Awake<T1, T2, T3>(Disposer disposer, T1 t1, T2 t2, T3 t3)
    {
      this.Add(disposer);
      IObjectSystem objectSystem;
      if (!this.dicObjectSystem.TryGetValue(disposer.GetType(), out objectSystem)) throw new Exception($"{disposer.GetType().Name} Not Find");

      IAwake<T1, T2, T3> awake = objectSystem as IAwake<T1, T2, T3>;
      if (awake == null) throw new Exception($"{disposer.GetType().Name} Not Inherit IAwake");
      objectSystem.Set(disposer);
      awake.Awake(t1, t2, t3);
    }
    #endregion

    #region Start & Update &LateUpdate

    public void Start()
    {
      while (qStart.Count > 0)
      {
        Disposer disposer = this.qStart.Dequeue();
        if (disposer.Id == 0) continue;
        IObjectSystem objectSystem;
        if (!this.dicObjectSystem.TryGetValue(disposer.GetType(), out objectSystem)) continue;

        IStart tmp = objectSystem as IStart;
        if (tmp == null) continue;

        objectSystem.Set(disposer);
        tmp.Start();
      }
    }

    public void Update()
    {

      this.Start();
      while (this.qUpdate.Count > 0)
      {
        Disposer disposer = this.qUpdate.Dequeue();
        if (disposer.Id == 0) continue;
        IObjectSystem objectSystem;
        if (!this.dicObjectSystem.TryGetValue(disposer.GetType(), out objectSystem)) continue;

        IUpdate update = objectSystem as IUpdate;
        if (update == null) continue;

        objectSystem.Set(disposer);
        this.qUpdate2.Enqueue(disposer);
        try
        {
          update.Update();
        }
        catch (Exception e)
        {

          Debug.LogError(e.ToString());
        }
      }
      ObjectHelper.Swap(ref this.qUpdate, ref this.qUpdate2);
    }

    public void LateUpdate()
    {
      while (this.qLateUpdate.Count > 0)
      {
        Disposer disposer = this.qLateUpdate.Dequeue();

        if (disposer.Id == 0) continue;

        IObjectSystem objectSystem;
        if (!this.dicObjectSystem.TryGetValue(disposer.GetType(), out objectSystem)) continue;

        ILateUpdate update = objectSystem as ILateUpdate;
        if (update == null) continue;

        objectSystem.Set(disposer);
        this.QLateUpdate2.Enqueue(disposer);
        try
        {
          update.LateUpdate();
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
      ObjectHelper.Swap(ref this.qLateUpdate, ref this.QLateUpdate2);
    }

    #endregion



    #endregion



    #region 获取程序集
    public Assembly GetAssembly(DllType dllType)
    {
      return this.dicAssembles[dllType];
    }

    public Assembly[] GetAllAssembly()
    {
      return this.dicAssembles.Values.ToArray();
    }
    #endregion






  }
}


